package pers.duorhs.grgbusstation.amap;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pers.duorhs.grgbusstation.config.AmapApiConfig;
import pers.duorhs.grgbusstation.domain.GeoLocation;
import pers.duorhs.grgbusstation.domain.StationInfo;
import pers.duorhs.grgbusstation.domain.WalkingPlan;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 高德地图API调用工具类
 *
 * @author Duorhs
 * @date 2022/7/26 10:06
 */
@Slf4j
@Component
public class AmapApiHelper {
    private static final String SUCCESS = "1";
    private static final String FAILED = "0";
    private final AmapApiConfig apiConfig;

    @Autowired
    public AmapApiHelper(AmapApiConfig apiConfig) {
        this.apiConfig = apiConfig;
    }

    /**
     * 转化为弧度(rad)
     */
    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 查询地址经纬度信息
     *
     * @param address 搜索字段
     * @param city    所在城市
     * @return 查询的结果集
     */
    public List<GeoLocation> apiGeoInfo(String address, String city) {
        HttpRequest request = HttpUtil.createGet(apiConfig.getGeoInfoUrl(), true);
        Map<String, Object> param = MapUtil.newHashMap(3);
        param.put("key", apiConfig.getKey());
        param.put("address", address);
        param.put("city", city);
        request = request.form(param);
        HttpResponse response = request.execute();
        JSONObject jsonObject = JSON.parseObject(response.body());
        List<GeoLocation> list = new ArrayList<>();
        if (SUCCESS.equals(jsonObject.getString("status"))) {
            JSONArray geocodes = jsonObject.getJSONArray("geocodes");
            for (Object geocode : geocodes) {
                if (geocode instanceof JSONObject) {
                    String formattedAddress = ((JSONObject) geocode).getString("formatted_address");
                    String location = ((JSONObject) geocode).getString("location");
                    GeoLocation geoLocation = new GeoLocation(formattedAddress, location);
                    list.add(geoLocation);
                }
            }
        } else {
            log.info("请求返回失败,请求地址为:{},info: {}", address, jsonObject.getString("info"));
        }
        return list;
    }

    /**
     * 先根据经纬度筛选最近(3公里以内)的N个站点
     *
     * @param start        起点
     * @param destinations 所有站点的总集
     * @return 筛选后的站点列表
     */
    @Deprecated
    public List<StationInfo> nearbyStations(GeoLocation start, List<StationInfo> destinations) {
        //赤道半径(单位m)
        double EARTH_RADIUS = 6371000;
        ArrayList<StationInfo> nearby = new ArrayList<>();
        for (StationInfo destination : destinations) {
            String[] s = start.getGeoLocation().split(",");
            String[] e = destination.getGeoLocation().split(",");
            double radLat1 = rad(Double.parseDouble(s[0]));
            double radLat2 = rad(Double.parseDouble(e[0]));
            double a = radLat1 - radLat2;
            double b = rad(Double.parseDouble(s[1])) - rad(Double.parseDouble(e[1]));
            double dis = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
            dis = dis * EARTH_RADIUS;
            dis = Math.round(dis * 10000) / 10000;
            if (dis < 3000) {
                nearby.add(destination);
            }
        }
        return nearby;
    }

    /**
     * 规划步行路线
     *
     * @param start        起点
     * @param destinations 附件的班车站点列表
     * @return 返回规划列表
     */
    @Deprecated
    public ArrayList<WalkingPlan> apiWalkingPlan(GeoLocation start, List<StationInfo> destinations) {
        ArrayList<WalkingPlan> plans = new ArrayList<>();
        for (StationInfo destination : destinations) {
            WalkingPlan plan = new WalkingPlan();
            plan.setStart(start);
            plan.setEnd(destination);
            HttpRequest request = HttpUtil.createGet(apiConfig.getWalkingPlanUrl(), true);
            Map<String, Object> param = MapUtil.newHashMap(3);
            param.put("key", apiConfig.getKey());
            param.put("isindoor", "0");
            param.put("origin", start.getGeoLocation());
            param.put("destination", destination.getGeoLocation());
            param.put("show_fields", "status,info,infocode,count,route,cost");
            request = request.form(param);
            HttpResponse response = request.execute();
            JSONObject jsonObject = JSON.parseObject(response.body());

            if (SUCCESS.equals(jsonObject.getString("status"))) {
                JSONObject route = jsonObject.getJSONObject("route");
                JSONArray paths = route.getJSONArray("paths");
                for (Object path : paths) {
                    if (path instanceof JSONObject) {
                        String distance = ((JSONObject) path).getString("distance");
                        String duration = ((JSONObject) path).getJSONObject("cost").getString("duration");
                        plan.setDuration(Long.parseLong(duration));
                        plan.setDistance(Long.parseLong(distance));
                    }
                }
                plans.add(plan);
            } else {
                log.info("请求返回失败,info: {}", jsonObject.getString("info"));
            }
        }

        plans.sort((o1, o2) -> (int) (o1.getDuration() - o2.getDuration()));
        String jsonString = JSON.toJSONString(plans);
        File result = new File("result\\" + start.getName() + "-" + DateUtil.today() + ".json");
        FileUtil.writeString(jsonString, result, StandardCharsets.UTF_8);
        return plans;
    }
}
